﻿using Microsoft.ApplicationServer.Caching;
using Microsoft.Ted.Wacel.TableStorage;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Ted.Wacel
{
    public class Table<E> : CloudTableStorage<E> where E : new()
    {
        DataCache mCache = null;

        public Table(string idFieldName, string connectionString, string tableName) :
            this(idFieldName, idFieldName, connectionString, tableName)
        {

        }
        public Table(string partitionFieldName, string idFieldName, string connectionString, string tableName) :
            base(partitionFieldName, idFieldName, connectionString, tableName, true)
        {

        }
        public Table(string idFieldName, string connectionString, string tableName, DataCache cache):
            this(idFieldName, idFieldName, connectionString, tableName, cache)
        {
        }
        public Table(string partitionFieldName, string idFieldName, string connectionString, string tableName, DataCache cache) :
            base(partitionFieldName, idFieldName, connectionString, tableName, true)
        {
            mCache = cache;
        }

        public override void Clear()
        {
            if (mCache != null)
                mCache.Clear();
            base.Clear();
        }

        public override string Insert(E entity)
        {
            var ETag = base.Insert(entity);
            if (mCache != null)
                putInCache(entity, ETag);
            return ETag;
        }

        private void putInCache(E entity, string ETag)
        {
             var rowId = GetRowKey(entity);
                if (ArePartitionKeyRowKeyDifferent)
                {
                    var partitionId = GetPartitionKey(entity);
                    mCache.CreateRegion(partitionId);
                    mCache.Put(rowId, entity, new DataCacheTag[]{new DataCacheTag(ETag), new DataCacheTag("_*_")}, partitionId);
                }
                else
                    mCache.Put(rowId, entity, new DataCacheTag[]{new DataCacheTag(ETag), new DataCacheTag("_*_")});
        }
        private void removeFromCache(string id, string partitionId)
        {
            if (ArePartitionKeyRowKeyDifferent && !string.IsNullOrEmpty(partitionId))
            {
                mCache.Remove(id, partitionId);
            }
            else
                mCache.Remove(id);
        }

        private string  getEtag(string id, string partitionId)
        {
            if (mCache != null)
            {
                DataCacheItem entity = null;
                if (ArePartitionKeyRowKeyDifferent && !string.IsNullOrEmpty(partitionId))
                    entity = mCache.GetCacheItem(id, partitionId);
                else
                    entity = mCache.GetCacheItem(id);
                if (entity != null)
                {
                    if (entity.Tags.Count > 0)
                        return entity.Tags[0].ToString();
                    else
                        return "";
                }
                else
                    return "";
            }
            else
                return "";
        }
        public override E Read(string id, string partitionId = null)
        {
            string ETag = null;
            return this.Read(id, out ETag, partitionId);
        }
        public override E Read(string id, out string Etag, string partitionId = null)
        {
            if (mCache != null)
            {
                DataCacheItem entity = null;
                if (ArePartitionKeyRowKeyDifferent && !string.IsNullOrEmpty(partitionId))
                    entity = mCache.GetCacheItem(id, partitionId);
                else
                    entity = mCache.GetCacheItem(id);
                if (entity != null)
                {
                    if (entity.Tags.Count > 0)
                        Etag = entity.Tags[0].ToString();
                    else
                        Etag = "";
                    return (E)entity.Value;
                }
            }
            return base.Read(id, out Etag, partitionId);
        }

        public override string Replace(E entity)
        {
            var Etag = base.Replace(entity);
            putInCache(entity, Etag);
            return Etag;
        }
        public override string Update(E entity, string Etag)
        {
            var newTag = base.Update(entity, Etag);
            putInCache(entity, newTag);
            return newTag;
        }
        public string Update(E entity)
        {
             var rowId = GetRowKey(entity);
             var partitionId = "";
             if (ArePartitionKeyRowKeyDifferent)
                 partitionId = GetPartitionKey(entity);
             else
                 partitionId = rowId;
             var tag = getEtag(rowId, partitionId);
             return Update(entity, tag);
        }
        public override void Delete(string id, string partitionId = null)
        {
            base.Delete(id, partitionId);
            removeFromCache(id, partitionId);
        }

        public override IEnumerable<E> List(string startId, string endId, string startPartitionId = null, string endPartitionId = null)
        {
            string pageKey = string.Format("<{0}><{1}><{2}><{3}>", startId, endId, startPartitionId, endPartitionId);

            if (mCache != null)
            {
                var obj = mCache.Get(pageKey);
                if (obj != null)
                    return (IEnumerable<E>)obj;
                else
                {
                    var list = base.List(startId, endId, startPartitionId, endPartitionId);
                    mCache.Put(pageKey, list);
                    return list;
                }
            }
            return base.List(startId, endId, startPartitionId, endPartitionId);
        }
    }
}
